package libpcapInactiveHandleMain

/**
  非活动句柄模式,支持新的libpcap功能
*/
import (
	"github.com/google/gopacket"
	"github.com/google/gopacket/layers"
	"github.com/google/gopacket/pcap"
	"goproduct/config"
	"goproduct/util"
	"log"
	"net"
	"strconv"
	"strings"
	"sync"
	"syscall"
	"time"
)

/**
base
*/
var (
	handleReceive   *pcap.Handle
	err             error
	fds             int
	redirectPortStr string
)

/**
conf.ini
*/
var (
	deviceIdReceive string
	blockMode       int //block=0,redirect=1
	blockIp         string
	whiteIp         string
	redirectPort    = make(map[uint16]int)
	redirectPage    string
)

var addr = new(syscall.SockaddrInet4)
var (
	ipv4Pool = sync.Pool{
		New: func() interface{} {
			return &layers.IPv4{
				Protocol:   layers.IPProtocolTCP,
				Id:         10000,
				Version:    4, //must
				IHL:        5,
				TOS:        0,
				TTL:        64,
				Checksum:   0,
				FragOffset: 0,
			}
		},
	}
	tcpPool = sync.Pool{
		New: func() interface{} {
			return &layers.TCP{}
		},
	}
)

//Packet buffer, SerializeLayers Clears the given write buffer
var (
	buffer  = gopacket.NewSerializeBuffer()
	Payload = gopacket.Payload("kb_iot")
)

func init() {
	//1.0 读取配置文件
	parser := config.InitConfig()
	fds, err = syscall.Socket(syscall.AF_INET, syscall.SOCK_RAW, syscall.IPPROTO_RAW)
	if err != nil {
		log.Fatalln("Failed to create the socket. Procedure: ", err)
	}
	//2.0 获取接收数据的网卡,并生成活动句柄
	deviceIdReceive = parser.GetString("common", "deviceIdReceive")
	var inactiveHandle *pcap.InactiveHandle
	inactiveHandle, err = pcap.NewInactiveHandle(
		deviceIdReceive, // network device
	)
	if err != nil {
		log.Fatalln("Mirror Handler error: ", err)
	}
	defer inactiveHandle.CleanUp()
	//3.0 套接字绑定网卡
	deviceIdSend := util.GetDeviceId()
	err = syscall.BindToDevice(fds, deviceIdSend)
	if err != nil {
		log.Fatalln("The socket failed to bind the network adapter. Procedure: ", err)
	}

	//7.0 套接字绑定网卡
	//7.1 设置句柄的立即模式,在立即模式下，数据包一到达就传送到应用程序。
	err = inactiveHandle.SetImmediateMode(true)
	if err != nil {
		log.Fatalln("The handle failed to set the immediate mode: ", err)
	}
	//7.2 设置句柄的混杂模式
	err = inactiveHandle.SetPromisc(true)
	if err != nil {
		log.Fatalln("The handle failed to set promiscuous mode. Procedure: ", err)
	}
	//7.3 设置句柄的要捕获的每个数据包的最大字节数
	err = inactiveHandle.SetSnapLen(65535)
	if err != nil {
		log.Fatalln("Failed to set the maximum number of bytes per packet to be captured for the handle: ", err)
	}
	//8.0 激活句柄
	handleReceive, err = inactiveHandle.Activate() // 在此之后，inactive 不再有效
	if err != nil {
		log.Fatalln("Failed to activate the handle: ", err)
	}
	log.Println("The program has been started")
	log.Println("当前deviceIdReceive:", deviceIdReceive)
	log.Println("当前deviceIdSend:", deviceIdSend)
	// 定时读取配置文件黑白名单
	go func() {
		for true {
			parser = config.InitConfig()
			blockIpNew := parser.GetString("block", "black_list")
			whiteIpNew := parser.GetString("block", "white_list")
			redirectPageNew := parser.GetString("block", "redirect_page")
			blockModeNew := int(parser.GetUint32("block", "block_mode"))
			redirectPortNew := parser.GetString("block", "redirect_port")
			if !strings.EqualFold(blockIp, blockIpNew) {
				blockIp = blockIpNew
				bpf := util.CreateBPF(blockIp)
				err = handleReceive.SetBPFFilter(bpf)
				if err != nil {
					log.Println("Description Failed to set the BPF: ", err)
				}
				log.Println("当前bpf:", bpf)
				log.Println("当前阻断IP:", blockIp)
			}
			if !strings.EqualFold(whiteIp, whiteIpNew) {
				whiteIp = whiteIpNew
				log.Println("当前white IP:", whiteIp)
			}
			if !strings.EqualFold(redirectPage, redirectPageNew) {
				redirectPage = redirectPageNew
				log.Println("当前redirect page:", redirectPage)
			}
			if blockMode != blockModeNew {
				if blockModeNew != 0 && blockModeNew != 1 {
					log.Println("Enter the correct block_mode range 0,1; 0->block,1->redirect")
				} else {
					blockMode = blockModeNew
					if blockMode == 0 {
						//block
						log.Println("当前block mode:", blockMode, "->block")
					} else {
						//redirect
						log.Println("当前block mode:", blockMode, "->redirect")
					}
				}
			}

			if !strings.EqualFold(redirectPortStr, redirectPortNew) {
				redirectPortStr = redirectPortNew
				ports := strings.Split(redirectPortStr, ",")
				for i := range ports {
					v, err2 := strconv.Atoi(ports[i])
					if err2 != nil {
						log.Println("重定向端口出现非数字", err2)
					} else {
						redirectPort[uint16(v)] = 0
					}
				}
				log.Println("当前redirect port:", redirectPortStr)
			}
			time.Sleep(5 * time.Second)
		}
	}()
}
func Main() {
	var eth layers.Ethernet
	var dot1q layers.Dot1Q
	var ipv4 layers.IPv4
	var tcp layers.TCP
	var palody gopacket.Payload

	dlc := gopacket.DecodingLayerContainer(gopacket.DecodingLayerArray(nil))
	dlc = dlc.Put(&eth)
	dlc = dlc.Put(&dot1q)
	dlc = dlc.Put(&ipv4)
	dlc = dlc.Put(&tcp)
	dlc = dlc.Put(&palody)
	parserD := dlc.LayersDecoder(layers.LayerTypeEthernet, gopacket.NilDecodeFeedback)
	decoded := make([]gopacket.LayerType, 0, 20)
	defer syscall.Close(fds)
	defer handleReceive.Close()
	for true {
		//Generate the array one byte at a time,Returns a pointer to a byte array,
		//Each call to ZeroCopyReadPacketData invalidates any data previously returned by ZeroCopyReadPacketData
		data, _, _ := handleReceive.ZeroCopyReadPacketData()
		//Parse the IP and TCP information in the packet
		var lt interface{}
		lt, err = parserD(data, &decoded)
		if err != nil {
			continue
		}
		if lt != gopacket.LayerTypeZero {
			continue
		}
		//Incomplete packets
		if ipv4.SrcIP == nil || ipv4.DstIP == nil {
			continue
		}
		//Whitelist selection
		if util.GetIpsMaskContent(whiteIp, ipv4.SrcIP.String()) || util.GetIpsMaskContent(whiteIp, ipv4.DstIP.String()) {
			continue
		}
		//Blacklist filtering
		if !util.GetIpsMaskContent(blockIp, ipv4.SrcIP.String()) && !util.GetIpsMaskContent(blockIp, ipv4.DstIP.String()) {
			continue
		}
		sendPacket(ipv4, tcp, blockMode)
	}
}

func sendPacket(ipv4 layers.IPv4, tcp layers.TCP, blockMode int) {
	if tcp.SYN && !tcp.ACK {
		//伪造第二次握手, seq = 随机值。Ack_seq = old_seq + 1
		SendSYNACK(ipv4.DstIP, ipv4.SrcIP, tcp.DstPort, tcp.SrcPort, tcp.Seq+1)
	}
	if tcp.SYN || tcp.FIN || tcp.RST {
		return
	}
	if blockMode == 1 {
		if _, ok := redirectPort[uint16(tcp.DstPort)]; ok {
			SendRedirect(ipv4.DstIP, ipv4.SrcIP, tcp.DstPort, tcp.SrcPort, tcp.Ack, tcp.Seq+uint32(len(tcp.Payload)))
		}
	}
	if blockMode == 0 {
		//阻断主要是发送的SEQ序列号和上一位的ACK值相等
		SendRST(ipv4.DstIP, ipv4.SrcIP, tcp.DstPort, tcp.SrcPort, tcp.Ack)
	}
}

func SendRST(srcIp net.IP, dstIp net.IP, srcPort layers.TCPPort, dstPort layers.TCPPort, ack uint32) {
	ipv4 := ipv4Pool.Get().(*layers.IPv4)
	ipv4.SrcIP = srcIp
	ipv4.DstIP = dstIp
	tcp := tcpPool.Get().(*layers.TCP)
	tcp.SrcPort = srcPort
	tcp.DstPort = dstPort
	tcp.RST = true
	tcp.PSH = true
	tcp.Seq = ack
	tcp.Ack = 10000

	if err = tcp.SetNetworkLayerForChecksum(ipv4); err != nil {
		log.Fatalln(err)
		return
	}
	err = gopacket.SerializeLayers(
		buffer,
		gopacket.SerializeOptions{ComputeChecksums: true, FixLengths: true},
		ipv4,
		tcp,
		Payload)
	if err != nil {
		log.Fatalln(err)
		return
	}
	addr.Addr = InetAton(ipv4.DstIP)
	addr.Port = int(tcp.DstPort)
	err = syscall.Sendto(fds, buffer.Bytes(), 0, addr)
	if err != nil {
		log.Fatalln(err)
		return
	}
}
func SendSYNACK(srcIp net.IP, dstIp net.IP, srcPort layers.TCPPort, dstPort layers.TCPPort, seq uint32) {
	ipv4c := layers.IPv4{
		SrcIP:      srcIp,
		DstIP:      dstIp,
		Protocol:   layers.IPProtocolTCP,
		Id:         10000,
		Version:    4, //must
		IHL:        5,
		TOS:        0,
		TTL:        64,
		Checksum:   0,
		FragOffset: 0,
	}
	tcpc := layers.TCP{
		SrcPort: srcPort,
		DstPort: dstPort,
		SYN:     true,
		ACK:     true,
		RST:     false,
		Seq:     10000002,
		Ack:     seq,
	}
	if err = tcpc.SetNetworkLayerForChecksum(&ipv4c); err != nil {
		log.Fatalln(err)
		return
	}
	err = gopacket.SerializeLayers(
		buffer,
		gopacket.SerializeOptions{ComputeChecksums: true, FixLengths: true},
		&ipv4c,
		&tcpc,
	)
	if err != nil {
		log.Fatalln(err)
		return
	}
	addr.Addr = InetAton(ipv4c.DstIP)
	addr.Port = int(tcpc.DstPort)
	err = syscall.Sendto(fds, buffer.Bytes(), 0, addr)
	if err != nil {
		log.Fatalln(err)
		return
	}
}
func SendRedirect(srcIp net.IP, dstIp net.IP, srcPort layers.TCPPort, dstPort layers.TCPPort, ack, len uint32) {
	ipv4 := layers.IPv4{
		SrcIP:      srcIp,
		DstIP:      dstIp,
		Protocol:   layers.IPProtocolTCP,
		Id:         10000,
		Version:    4, //must
		IHL:        5,
		TOS:        0,
		TTL:        64,
		Checksum:   0,
		FragOffset: 0,
	}
	tcp := layers.TCP{
		SrcPort: srcPort,
		DstPort: dstPort,
		ACK:     true,
		PSH:     false,
		Seq:     ack,
		Ack:     len,
	}
	addr.Addr = InetAton(ipv4.DstIP)
	addr.Port = int(tcp.DstPort)

	if err = tcp.SetNetworkLayerForChecksum(&ipv4); err != nil {
		log.Fatalln(err)
	}

	//组确认包
	err = gopacket.SerializeLayers(
		buffer,
		gopacket.SerializeOptions{ComputeChecksums: true, FixLengths: true},
		&ipv4,
		&tcp,
	)
	if err != nil {
		log.Fatalln("Failed to confirm the group package", err)
	}
	//发送确认包
	err = syscall.Sendto(fds, buffer.Bytes(), 0, addr)
	if err != nil {
		log.Fatalln("Failed to send the confirmation packet", err)
	}

	//组重定向包
	ipv4.Id += 1
	tcp.ACK = true
	tcp.FIN = true
	tcp.PSH = true
	tcp.RST = false
	if err = tcp.SetNetworkLayerForChecksum(&ipv4); err != nil {
		log.Fatalln(err)
	}
	err = buffer.Clear()
	if err != nil {
		log.Fatalln("Failed to clear packets", err)
	}
	var buffer2 = gopacket.NewSerializeBuffer()
	err = gopacket.SerializeLayers(
		buffer2,
		gopacket.SerializeOptions{ComputeChecksums: true, FixLengths: true},
		&ipv4,
		&tcp,
		gopacket.Payload("HTTP/1.1 302 Moved Temporarily\r\nLocation: "+redirectPage+"\r\n\r\n"),
	)
	if err != nil {
		log.Fatalln("Failed to redirect the group package", err)
	}
	err = syscall.Sendto(fds, buffer2.Bytes(), 0, addr)
	if err != nil {
		log.Fatalln("Failed to send the redirection packet. Procedure", err)
	}
}

func InetAton(ip net.IP) [4]byte {
	bits := strings.Split(ip.String(), ".")
	ip.MarshalText()
	b0, _ := strconv.Atoi(bits[0])
	b1, _ := strconv.Atoi(bits[1])
	b2, _ := strconv.Atoi(bits[2])
	b3, _ := strconv.Atoi(bits[3])
	return [4]byte{byte(b0), byte(b1), byte(b2), byte(b3)}
}
